Python च्या Hypothesis लायब्ररीसह प्रॉपर्टी-आधारित चाचणी शोधा. उदाहरण-आधारित चाचण्यांच्या पलीकडे जाऊन एज केसेस शोधा आणि अधिक मजबूत, विश्वसनीय सॉफ्टवेअर तयार करा.
युनिट चाचण्यांच्या पलीकडे: Python च्या Hypothesis सह प्रॉपर्टी-आधारित चाचणीमध्ये सखोल अभ्यास
सॉफ्टवेअर डेव्हलपमेंटच्या जगात, चाचणी ही गुणवत्तेचा आधारस्तंभ आहे. दशकानुंदशके, उदाहरण-आधारित चाचणी हे प्रमुख प्रतिमान राहिले आहे. आम्ही बारकाईने इनपुट तयार करतो, अपेक्षित आउटपुट परिभाषित करतो आणि आमचा कोड योजनेनुसार कार्य करतो हे सत्यापित करण्यासाठी विधान लिहितो. unittest
आणि pytest
सारख्या फ्रेमवर्कमध्ये आढळणारा हा दृष्टीकोन शक्तिशाली आणि आवश्यक आहे. परंतु जर मी तुम्हाला सांगितले की एक पूरक दृष्टीकोन आहे जो आपल्याला बग उघड करू शकतो ज्यांचा आपण कधी विचारही केला नाही?
प्रॉपर्टी-आधारित चाचणी जगात आपले स्वागत आहे, एक प्रतिमान जे विशिष्ट उदाहरणांची चाचणी करण्याऐवजी आपल्या कोडच्या सामान्य गुणधर्मांचे सत्यापन करण्यावर लक्ष केंद्रित करते. आणि Python इकोसिस्टममध्ये, या दृष्टीकोनाचा निर्विवाद चॅम्पियन Hypothesis नावाचे लायब्ररी आहे.
हे सर्वसमावेशक मार्गदर्शक आपल्याला Hypothesis सह प्रॉपर्टी-आधारित चाचणीच्या संपूर्ण नवशिक्यापासून ते आत्मविश्वासू तज्ञ बनवेल. आम्ही मूळ संकल्पना शोधू, व्यावहारिक उदाहरणांमध्ये सखोल अभ्यास करू आणि अधिक मजबूत, विश्वसनीय आणि बग-प्रतिरोधक सॉफ्टवेअर तयार करण्यासाठी हे शक्तिशाली साधन आपल्या दैनंदिन विकास कार्यप्रवाहात कसे समाकलित करावे हे शिकू.
प्रॉपर्टी-आधारित चाचणी म्हणजे काय? दृष्टिकोन बदलणे
Hypothesis समजून घेण्यासाठी, आपल्याला प्रथम प्रॉपर्टी-आधारित चाचणीचा मूलभूत विचार समजून घेणे आवश्यक आहे. चला त्याची तुलना पारंपरिक उदाहरण-आधारित चाचणीशी करूया जी आपल्याला माहित आहे.
उदाहरण-आधारित चाचणी: परिचित मार्ग
कल्पना करा की आपण एक सानुकूल सॉर्टिंग फंक्शन my_sort()
लिहिले आहे. उदाहरण-आधारित चाचणीसह, आपली विचार प्रक्रिया अशी असेल:
- "चला त्याची साध्या, क्रमवारी लावलेल्या सूचीसह चाचणी करूया." ->
assert my_sort([1, 2, 3]) == [1, 2, 3]
- "उलट-क्रमवारी लावलेल्या सूचीबद्दल काय?" ->
assert my_sort([3, 2, 1]) == [1, 2, 3]
- "रिक्त सूचीबद्दल काय?" ->
assert my_sort([]) == []
- "डुप्लिकेट असलेली सूची?" ->
assert my_sort([5, 1, 5, 2]) == [1, 2, 5, 5]
- "आणि नकारात्मक संख्या असलेली सूची?" ->
assert my_sort([-1, -5, 0]) == [-5, -1, 0]
हे प्रभावी आहे, परंतु त्यात एक मूलभूत मर्यादा आहे: आपण ज्या प्रकरणांचा विचार करू शकता त्यांचीच आपण चाचणी घेत आहात. आपल्या चाचण्या आपल्या कल्पनाशक्तीइतक्याच चांगल्या आहेत. आपण मोठ्या संख्येचा समावेश असलेल्या एज केसेस, फ्लोटिंग-पॉइंट त्रुटी, विशिष्ट युनिकोड वर्ण किंवा डेटाचे जटिल संयोजन गमावू शकता ज्यामुळे अनपेक्षित वर्तन होऊ शकते.
प्रॉपर्टी-आधारित चाचणी: अपरिवर्तनीयांमध्ये विचार करणे
प्रॉपर्टी-आधारित चाचणी स्क्रिप्ट फिरवते. विशिष्ट उदाहरणे प्रदान करण्याऐवजी, आपण आपल्या फंक्शनचे गुणधर्म किंवा अपरिवर्तनीय परिभाषित करता—नियम जे कोणत्याही वैध इनपुटसाठी खरे असले पाहिजेत. आमच्या my_sort()
फंक्शनसाठी, हे गुणधर्म असे असू शकतात:
- आउटपुट क्रमवारी लावलेले आहे: संख्यांच्या कोणत्याही सूचीसाठी, आउटपुट सूचीमधील प्रत्येक घटक त्याच्या पुढील घटकापेक्षा कमी किंवा समान असतो.
- आउटपुटमध्ये इनपुटप्रमाणेच घटक आहेत: क्रमवारी लावलेली सूची ही मूळ सूचीचा फक्त एक क्रमचय आहे; कोणतेही घटक जोडले किंवा गमावले जात नाहीत.
- फंक्शन आयडेमपोटेंट आहे: आधीपासून क्रमवारी लावलेल्या सूचीला क्रमवारी लावल्याने त्यात बदल होऊ नये. म्हणजेच,
my_sort(my_sort(some_list)) == my_sort(some_list)
.
या दृष्टीकोणासह, आपण चाचणी डेटा लिहित नाही. आपण नियम लिहित आहात. त्यानंतर आपण Hypothesis सारख्या फ्रेमवर्कला शेकडो किंवा हजारो यादृच्छिक, विविध आणि बर्याचदा विक्षिप्त इनपुट तयार करू द्या आणि आपले गुणधर्म चुकीचे असल्याचे सिद्ध करण्याचा प्रयत्न करू द्या. जर त्यास एखादे इनपुट सापडले जे मालमत्ता खंडित करते, तर त्यास एक बग सापडला आहे.
Hypothesis सादर करत आहोत: आपले स्वयंचलित चाचणी डेटा जनरेटर
Hypothesis हे Python साठी प्रमुख प्रॉपर्टी-आधारित चाचणी लायब्ररी आहे. आपण परिभाषित केलेल्या गुणधर्मानुसार हे चाचणी डेटा तयार करण्याचे कठोर परिश्रम घेते. हे केवळ यादृच्छिक डेटा जनरेटर नाही; हे एक बुद्धिमान आणि शक्तिशाली साधन आहे जे कार्यक्षमतेने बग शोधण्यासाठी डिझाइन केलेले आहे.
Hypothesis ची प्रमुख वैशिष्ट्ये
- स्वयंचलित चाचणी प्रकरण निर्मिती: आपण आवश्यक असलेल्या डेटाचा *आकार* परिभाषित करता (उदा. "पूर्णांकांची सूची," "केवळ अक्षरे असलेला स्ट्रिंग," "भविष्यातील तारीखवेळ"), आणि Hypothesis त्या आकारानुसार विस्तृत प्रकारची उदाहरणे तयार करते.
- बुद्धिमान संकोचन: हे जादूचे वैशिष्ट्य आहे. जेव्हा Hypothesis ला अयशस्वी चाचणी प्रकरण आढळते (उदा. 50 जटिल संख्यांची सूची जी आपल्या क्रमवारी फंक्शनला क्रॅश करते), तेव्हा ते केवळ त्या मोठ्या सूचीचा अहवाल देत नाही. ते अयशस्वी होण्याचे कारण असलेल्या लहान शक्य उदाहरण शोधण्यासाठी इनपुटला बुद्धिमानीने आणि स्वयंचलितपणे सोपे करते. 50-घटकांच्या सूचीऐवजी, ते अहवाल देऊ शकते की अयशस्वीता फक्त
[inf, nan]
सह उद्भवते. हे डीबगिंग अत्यंत वेगवान आणि कार्यक्षम बनवते. - अखंड एकत्रीकरण: Hypothesis
pytest
आणिunittest
सारख्या लोकप्रिय चाचणी फ्रेमवर्कसह उत्तम प्रकारे समाकलित होते. आपण आपला कार्यप्रवाह न बदलता आपल्या विद्यमान उदाहरण-आधारित चाचण्यांच्या बाजूला प्रॉपर्टी-आधारित चाचण्या जोडू शकता. - स्ट्रॅटेजीची समृद्ध लायब्ररी: हे साधे पूर्णांक आणि स्ट्रिंगपासून ते जटिल, नेस्टेड डेटा स्ट्रक्चर्स, टाइमझोन-जागरूक डेटटाइम्स आणि NumPy ॲरेपर्यंत सर्वकाही व्युत्पन्न करण्यासाठी अंगभूत "स्ट्रॅटेजी" च्या विस्तृत संग्रहासह येते.
- स्टेटफुल चाचणी: अधिक जटिल प्रणालींसाठी, Hypothesis राज्य संक्रमणामध्ये दोष शोधण्यासाठी कृतींचे क्रम तपासू शकते, जे उदाहरण-आधारित चाचणीसह कुप्रसिद्धपणे कठीण आहे.
प्रारंभ करणे: आपली पहिली Hypothesis चाचणी
चला आपले हात गलिच्छ करूया. Hypothesis समजून घेण्याचा उत्तम मार्ग म्हणजे ते प्रत्यक्ष कृतीत पाहणे.
स्थापना
प्रथम, आपल्याला Hypothesis आणि आपल्या आवडीचे चाचणी रनर स्थापित करणे आवश्यक आहे (आम्ही pytest
वापरू). हे तितकेच सोपे आहे:
pip install pytest hypothesis
एक साधे उदाहरण: परिपूर्ण मूल्य फंक्शन
एका साध्या फंक्शनचा विचार करूया जे एका संख्येचे परिपूर्ण मूल्य मोजण्यासाठी आहे. किंचित त्रुटीपूर्ण अंमलबजावणी यासारखी दिसू शकते:
# `my_math.py` नावाच्या फाईलमध्ये def custom_abs(x): """परिपूर्ण मूल्य फंक्शनची सानुकूल अंमलबजावणी.""" if x < 0: return -x return x
आता, एक चाचणी फाईल लिहूया, test_my_math.py
. प्रथम, पारंपरिक pytest
दृष्टीकोन:
# test_my_math.py (उदाहरण-आधारित) def test_abs_positive(): assert custom_abs(5) == 5 def test_abs_negative(): assert custom_abs(-5) == 5 def test_abs_zero(): assert custom_abs(0) == 0
या चाचण्या उत्तीर्ण होतात. या उदाहरणांवर आधारित आपले फंक्शन योग्य दिसते. परंतु आता, Hypothesis सह प्रॉपर्टी-आधारित चाचणी लिहूया. परिपूर्ण मूल्य फंक्शनची मुख्य मालमत्ता काय आहे? परिणाम कधीही नकारात्मक नसावा.
# test_my_math.py (Hypothesis सह प्रॉपर्टी-आधारित) from hypothesis import given from hypothesis import strategies as st from my_math import custom_abs @given(st.integers()) def test_abs_property_is_non_negative(x): """प्रॉपर्टी: कोणत्याही पूर्णांकाचे परिपूर्ण मूल्य नेहमी >= 0 असते.""" assert custom_abs(x) >= 0
चला हे खंडित करूया:
from hypothesis import given, strategies as st
: आम्ही आवश्यक घटक आयात करतो.given
हे एक सजावट आहे जे नियमित चाचणी फंक्शनला प्रॉपर्टी-आधारित चाचणीमध्ये बदलते.strategies
हे मॉड्यूल आहे जिथे आम्हाला आमचे डेटा जनरेटर मिळतात.@given(st.integers())
: हा चाचणीचा गाभा आहे.@given
सजावट Hypothesis ला हे चाचणी फंक्शन अनेक वेळा चालवण्यास सांगते. प्रत्येक रनसाठी, ते प्रदान केलेल्या स्ट्रॅटेजीst.integers()
वापरून एक मूल्य व्युत्पन्न करेल आणि ते आमच्या चाचणी फंक्शनला युक्तिवादx
म्हणून पास करेल.assert custom_abs(x) >= 0
: ही आपली मालमत्ता आहे. आम्ही जोर देतो की Hypothesis ने तयार केलेले कोणतेही पूर्णांकx
असले तरी, आमच्या फंक्शनचा परिणाम शून्यापेक्षा जास्त किंवा समान असणे आवश्यक आहे.
जेव्हा आपण हे pytest
सह चालवता, तेव्हा ते बर्याच मूल्यांसाठी उत्तीर्ण होण्याची शक्यता आहे. Hypothesis 0, -1, 1, मोठ्या सकारात्मक संख्या, मोठ्या नकारात्मक संख्या आणि बरेच काही वापरून पाहिल. आमचे साधे फंक्शन हे सर्व योग्यरित्या हाताळते. आता, दुर्बलता शोधण्यासाठी एक वेगळी स्ट्रॅटेजी वापरून पाहूया.
# चला फ्लोटिंग पॉइंट नंबरसह चाचणी करूया @given(st.floats()) def test_abs_floats_property(x): assert custom_abs(x) >= 0
जर आपण हे चालवले तर, Hypothesis ला लवकरच अयशस्वी प्रकरण सापडेल!
Falsifying example: test_abs_floats_property(x=nan) ... assert custom_abs(nan) >= 0 AssertionError: assert nan >= 0
Hypothesis ने शोधले की आपले फंक्शन, जेव्हा float('nan')
(नंबर नाही) दिले जाते, तेव्हा nan
परत करते. विधान nan >= 0
चुकीचे आहे. आम्हाला नुकताच एक सूक्ष्म बग सापडला आहे ज्याची आम्ही व्यक्तिचलितपणे चाचणी करण्याचा विचार केला नसेल. आम्ही हे प्रकरण हाताळण्यासाठी आपले फंक्शन निश्चित करू शकलो असतो, कदाचित ValueError
वाढवून किंवा विशिष्ट मूल्य परत करून.
अधिक चांगले, जर बग खूप विशिष्ट फ्लोटसह असेल तर काय? Hypothesis च्या श्रिंकरने एका मोठ्या, जटिल अयशस्वी संख्येला सर्वात सोप्या शक्य आवृत्तीमध्ये कमी केले असते जे अजूनही बगला ट्रिगर करते.
स्ट्रॅटेजीची शक्ती: आपला चाचणी डेटा तयार करणे
स्ट्रॅटेजी Hypothesis चा आत्मा आहेत. ते डेटा व्युत्पन्न करण्यासाठी पाककृती आहेत. लायब्ररीमध्ये अंगभूत स्ट्रॅटेजीची विस्तृत श्रेणी समाविष्ट आहे, आणि आपण कल्पना करू शकता अशा अक्षरशः कोणत्याही डेटा स्ट्रक्चर व्युत्पन्न करण्यासाठी आपण त्या एकत्र आणि सानुकूलित करू शकता.
सामान्य अंगभूत स्ट्रॅटेजी
- संख्यात्मक:
st.integers(min_value=0, max_value=1000)
: पूर्णांक व्युत्पन्न करते, वैकल्पिकरित्या विशिष्ट श्रेणीमध्ये.st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False)
: फ्लोट्स व्युत्पन्न करते, विशेष मूल्यांवर उत्कृष्ट-अनुदानित नियंत्रणासह.st.fractions()
,st.decimals()
- मजकूर:
st.text(min_size=1, max_size=50)
: एका विशिष्ट लांबीचे युनिकोड स्ट्रिंग व्युत्पन्न करते.st.text(alphabet='abcdef0123456789')
: विशिष्ट वर्ण संचामधून स्ट्रिंग व्युत्पन्न करते (उदा. हेक्स कोडसाठी).st.characters()
: वैयक्तिक वर्ण व्युत्पन्न करते.
- संग्रह:
st.lists(st.integers(), min_size=1)
: सूची व्युत्पन्न करते जिथे प्रत्येक घटक एक पूर्णांक असतो. लक्षात घ्या की आम्ही युक्तिवाद म्हणून दुसरी स्ट्रॅटेजी कशी पास करतो! याला रचना म्हणतात.st.tuples(st.text(), st.booleans())
: निश्चित संरचनेसह ट्युपल व्युत्पन्न करते.st.sets(st.integers())
st.dictionaries(keys=st.text(), values=st.integers())
: निर्दिष्ट की आणि मूल्य प्रकारांसह शब्दकोश व्युत्पन्न करते.
- तात्पुरते:
st.dates()
,st.times()
,st.datetimes()
,st.timedeltas()
. हे टाइमझोन-जागरूक केले जाऊ शकतात.
- विविध:
st.booleans()
:True
किंवाFalse
व्युत्पन्न करते.st.just('constant_value')
: नेहमी समान एकल मूल्य व्युत्पन्न करते. जटिल धोरणे तयार करण्यासाठी उपयुक्त.st.one_of(st.integers(), st.text())
: प्रदान केलेल्या धोरणांपैकी एकावरून मूल्य व्युत्पन्न करते.st.none()
: फक्तNone
व्युत्पन्न करते.
स्ट्रॅटेजी एकत्र करणे आणि रूपांतरित करणे
Hypothesis ची खरी शक्ती सोप्या धोरणांपासून जटिल धोरणे तयार करण्याच्या क्षमतेतून येते.
.map()
वापरणे
.map()
पद्धत आपल्याला एका धोरणातून एक मूल्य घेण्यास आणि त्यास दुसर्या गोष्टीत रूपांतरित करण्यास अनुमती देते. हे आपल्या सानुकूल वर्गांच्या वस्तू तयार करण्यासाठी योग्य आहे.
# एक साधा डेटा वर्ग from dataclasses import dataclass @dataclass class User: user_id: int username: str # वापरकर्ता वस्तू व्युत्पन्न करण्याची एक रणनीती user_strategy = st.builds( User, user_id=st.integers(min_value=1), username=st.text(min_size=3, alphabet='abcdefghijklmnopqrstuvwxyz') ) @given(user=user_strategy) def test_user_creation(user): assert isinstance(user, User) assert user.user_id > 0 assert user.username.isalpha()
.filter()
आणि assume()
वापरणे
कधीकधी आपल्याला काही व्युत्पन्न मूल्ये नाकारण्याची आवश्यकता असते. उदाहरणार्थ, आपल्याला पूर्णांकांची एक सूची आवश्यक असू शकते जिथे बेरीज शून्य नाही. आपण .filter()
वापरू शकता:
st.lists(st.integers()).filter(lambda x: sum(x) != 0)
तथापि, .filter()
वापरणे अक्षम असू शकते. अट वारंवार चुकीची असल्यास, Hypothesis ला वैध उदाहरण व्युत्पन्न करण्याचा प्रयत्न करण्यात बराच वेळ लागू शकतो. बर्याचदा एक चांगला दृष्टीकोन म्हणजे आपल्या चाचणी फंक्शनमध्ये assume()
वापरणे:
from hypothesis import assume @given(st.lists(st.integers())) def test_something_with_non_zero_sum_list(numbers): assume(sum(numbers) != 0) # ... येथे आपले चाचणी तर्क ...
assume()
Hypothesis ला सांगते: "जर ही अट पूर्ण झाली नाही, तर हे उदाहरण फक्त टाकून द्या आणि नवीन प्रयत्न करा." आपल्या चाचणी डेटाला मर्यादित करण्याचा हा अधिक थेट आणि बर्याचदा अधिक कार्यक्षम मार्ग आहे.
st.composite()
वापरणे
खर्या अर्थाने जटिल डेटा व्युत्पन्न करण्यासाठी जेथे एक व्युत्पन्न मूल्य दुसर्यावर अवलंबून असते, तेथे st.composite()
हे आवश्यक साधन आहे. हे आपल्याला एक फंक्शन लिहिण्याची परवानगी देते जे युक्तिवाद म्हणून एक विशेष draw
फंक्शन घेते, जे आपण इतर धोरणांमधून चरण-दर-चरण मूल्ये काढण्यासाठी वापरू शकता.
एक उत्कृष्ट उदाहरण म्हणजे सूची व्युत्पन्न करणे आणि त्या सूचीमध्ये एक वैध निर्देशांक.
@st.composite def list_and_index(draw): # प्रथम, एक गैर-रिक्त सूची काढा my_list = draw(st.lists(st.integers(), min_size=1)) # मग, एक निर्देशांक काढा जो त्या सूचीसाठी वैध असण्याची हमी आहे index = draw(st.integers(min_value=0, max_value=len(my_list) - 1)) return (my_list, index) @given(data=list_and_index()) def test_list_access(data): my_list, index = data # आम्ही धोरण कसे तयार केले या कारणामुळे हा प्रवेश सुरक्षित असण्याची हमी आहे element = my_list[index] assert element is not None # एक साधे विधान
प्रत्यक्ष कृतीत Hypothesis: वास्तविक जगातील परिस्थिती
चला या संकल्पना अधिक वास्तववादी समस्यांवर लागू करू ज्याचा सॉफ्टवेअर डेव्हलपर दररोज सामना करतात.
परिस्थिती 1: डेटा सिरीयललाइजेशन फंक्शनची चाचणी घेणे
एका फंक्शनची कल्पना करा जे वापरकर्ता प्रोफाइल (एक शब्दकोश) URL-सुरक्षित स्ट्रिंगमध्ये सिरीयल करते आणि दुसरे ते डिसेरियलाइज करते. एक मुख्य मालमत्ता म्हणजे प्रक्रिया पूर्णपणे उलट करता येण्यासारखी असावी.
import json import base64 def serialize_profile(data: dict) -> str: """एका शब्दकोशाला URL-सुरक्षित बेस 64 स्ट्रिंगमध्ये सिरीयल करते.""" json_string = json.dumps(data) return base64.urlsafe_b64encode(json_string.encode('utf-8')).decode('utf-8') def deserialize_profile(encoded_str: str) -> dict: """एका स्ट्रिंगला परत शब्दकोशामध्ये डिसेरियलाइज करते.""" json_string = base64.urlsafe_b64decode(encoded_str.encode('utf-8')).decode('utf-8') return json.loads(json_string) # आता चाचणीसाठी # आम्हाला JSON-सुसंगत शब्दकोश व्युत्पन्न करणारी रणनीती आवश्यक आहे json_dictionaries = st.dictionaries( keys=st.text(), values=st.recursive(st.none() | st.booleans() | st.floats(allow_nan=False) | st.text(), lambda children: st.lists(children) | st.dictionaries(st.text(), children), max_leaves=10) ) @given(profile=json_dictionaries) def test_serialization_roundtrip(profile): """प्रॉपर्टी: एन्कोड केलेले प्रोफाइल डिसेरियलाइज केल्याने मूळ प्रोफाइल परत मिळायला हवे.""" encoded = serialize_profile(profile) decoded = deserialize_profile(encoded) assert profile == decoded
ही एकल चाचणी आमच्या फंक्शन्सला डेटाच्या मोठ्या विविधतेसह हातोडा मारेल: रिक्त शब्दकोश, नेस्टेड सूची असलेले शब्दकोश, युनिकोड वर्ण असलेले शब्दकोश, विचित्र की असलेले शब्दकोश आणि बरेच काही. काही व्यक्तिचलित उदाहरणे लिहिण्यापेक्षा हे खूपच सखोल आहे.
परिस्थिती 2: क्रमवारी लावण्याच्या अल्गोरिदमची चाचणी घेणे
चला आपले क्रमवारी लावण्याचे उदाहरण पुन्हा पाहूया. आपण पूर्वी परिभाषित केलेल्या गुणधर्मांची चाचणी कशी घ्याल ते येथे आहे.
from collections import Counter def my_buggy_sort(numbers): # चला एक सूक्ष्म बग सादर करूया: ते डुप्लिकेट टाकते return sorted(list(set(numbers))) @given(st.lists(st.integers())) def test_sorting_properties(numbers): sorted_list = my_buggy_sort(numbers) # प्रॉपर्टी 1: आउटपुट क्रमवारी लावलेले आहे for i in range(len(sorted_list) - 1): assert sorted_list[i] <= sorted_list[i+1] # प्रॉपर्टी 2: घटक समान आहेत (यामुळे बग सापडेल) assert Counter(numbers) == Counter(sorted_list) # प्रॉपर्टी 3: फंक्शन आयडेमपोटेंट आहे assert my_buggy_sort(sorted_list) == sorted_list
जेव्हा आपण ही चाचणी चालवता, तेव्हा Hypothesis ला प्रॉपर्टी 2 साठी लवकरच अयशस्वी उदाहरण सापडेल, जसे की numbers=[0, 0]
. आपले फंक्शन [0]
परत करते आणि Counter([0, 0])
Counter([0])
च्या बरोबरीचे नाही. श्रिंकर हे सुनिश्चित करेल की अयशस्वी उदाहरण शक्य तितके सोपे आहे, ज्यामुळे बगचे कारण त्वरित स्पष्ट होते.
परिस्थिती 3: स्टेटफुल चाचणी
अंतर्गत स्थिती असलेल्या ऑब्जेक्ट्ससाठी जी वेळेनुसार बदलते (जसे की डेटाबेस कनेक्शन, शॉपिंग कार्ट किंवा कॅशे), दोष शोधणे अविश्वसनीयपणे कठीण असू शकते. दोष ट्रिगर करण्यासाठी ऑपरेशन्सचा एक विशिष्ट क्रम आवश्यक असू शकतो. Hypothesis या उद्देशासाठी `RuleBasedStateMachine` प्रदान करते.
मेमरीतील की-व्हॅल्यू स्टोअरसाठी एका साध्या API ची कल्पना करा:
class SimpleKeyValueStore: def __init__(self): self._data = {} def set(self, key, value): self._data[key] = value def get(self, key): return self._data.get(key) def delete(self, key): if key in self._data: del self._data[key] def size(self): return len(self._data)
आम्ही त्याचे वर्तन मॉडेल करू शकतो आणि स्टेट मशीनसह त्याची चाचणी घेऊ शकतो:
from hypothesis.stateful import RuleBasedStateMachine, rule, Bundle class KeyValueStoreMachine(RuleBasedStateMachine): def __init__(self): super().__init__() self.model = {} self.sut = SimpleKeyValueStore() # Bundle() चा वापर नियमांमधील डेटा पास करण्यासाठी केला जातो keys = Bundle('keys') @rule(target=keys, key=st.text(), value=st.integers()) def set_key(self, key, value): self.model[key] = value self.sut.set(key, value) return key @rule(key=keys) def delete_key(self, key): del self.model[key] self.sut.delete(key) @rule(key=st.text()) def get_key(self, key): model_val = self.model.get(key) sut_val = self.sut.get(key) assert model_val == sut_val @rule() def check_size(self): assert len(self.model) == self.sut.size() # चाचणी चालवण्यासाठी, आपण फक्त मशीन आणि unittest.TestCase मधून सबक्लास करा # pytest मध्ये, आपण फक्त चाचणी मशीन क्लासला नियुक्त करू शकता TestKeyValueStore = KeyValueStoreMachine.TestCase
Hypothesis आता `set_key`, `delete_key`, `get_key`, आणि `check_size` ऑपरेशन्सचे यादृच्छिक क्रम कार्यान्वित करेल, जे अथकपणे एक असा क्रम शोधण्याचा प्रयत्न करेल ज्यामुळे विधानांपैकी एक अयशस्वी होईल. हे तपासेल की हटवलेली की योग्यरित्या वागते की नाही, एकाधिक सेट आणि डिलीट नंतर आकार सुसंगत आहे की नाही आणि इतर अनेक परिस्थिती ज्यांची आपण व्यक्तिचलितपणे चाचणी करण्याचा विचार करू शकत नाही.
उत्तम पद्धती आणि प्रगत टिपा
- उदाहरण डेटाबेस: Hypothesis हुशार आहे. जेव्हा त्यास बग सापडतो, तेव्हा ते अयशस्वी उदाहरण स्थानिक निर्देशिकेत जतन करते (
.hypothesis/
). पुढच्या वेळी आपण आपल्या चाचण्या चालवता, तेव्हा ते अयशस्वी उदाहरण प्रथम पुन्हा प्ले करेल, जे आपल्याला त्वरित अभिप्राय देईल की बग अजूनही उपस्थित आहे. एकदा आपण ते निश्चित केले की, उदाहरण यापुढे पुन्हा प्ले केले जात नाही. @settings
सह चाचणी अंमलबजावणी नियंत्रित करणे: आपण@settings
सजावट वापरून चाचणी रनच्या बर्याच पैलू नियंत्रित करू शकता. आपण उदाहरणांची संख्या वाढवू शकता, एकच उदाहरण किती काळ चालू शकते याची अंतिम मुदत सेट करू शकता (अनंत लूप पकडण्यासाठी) आणि काही आरोग्य तपासण्या बंद करू शकता.@settings(max_examples=500, deadline=1000) # 500 उदाहरणे चालवा, 1-सेकंद अंतिम मुदत @given(...) ...
- अयशस्वीतांचे पुनरुत्पादन करणे: प्रत्येक Hypothesis रन एक सीड मूल्य मुद्रित करते (उदा.
@reproduce_failure('version', 'seed')
). जर CI सर्व्हरला असा बग सापडला जो आपण स्थानिक पातळीवर पुनरुत्पादित करू शकत नाही, तर आपण प्रदान केलेल्या सीडसह हे सजावट Hypothesis ला उदाहरणांचा अचूक क्रम चालवण्यास भाग पाडण्यासाठी वापरू शकता. - CI/CD सह एकत्रित करणे: Hypothesis कोणत्याही सतत एकत्रीकरण पाइपलाइनसाठी योग्य आहे. उत्पादन पोहोचण्यापूर्वी अस्पष्ट दोष शोधण्याची त्याची क्षमता त्याला एक अमूल्य सुरक्षा जाळी बनवते.
मानसिकता बदलणे: गुणधर्मांमध्ये विचार करणे
Hypothesis स्वीकारणे हे फक्त एक नवीन लायब्ररी शिकण्यापेक्षा अधिक आहे; हे आपल्या कोडच्या शुद्धतेबद्दल विचार करण्याच्या एका नवीन मार्गाला स्वीकारण्याबद्दल आहे. "मी कोणत्या इनपुटची चाचणी घ्यावी?" असे विचारण्याऐवजी, आपण "या कोडबद्दल सार्वत्रिक सत्य काय आहेत?" असे विचारण्यास सुरवात करता.
गुणधर्म ओळखण्याचा प्रयत्न करताना मार्गदर्शन करण्यासाठी येथे काही प्रश्न आहेत:
- उलट क्रिया आहे का? (उदा. सिरीयल/डिसेरियलाइज, एनक्रिप्ट/डिक्रिप्ट, कॉम्प्रेस/डिकॉम्प्रेस). मालमत्ता अशी आहे की ऑपरेशन आणि त्याची उलट क्रिया केल्याने मूळ इनपुट मिळायला हवे.
- ऑपरेशन आयडेमपोटेंट आहे का? (उदा.
abs(abs(x)) == abs(x)
). फंक्शन एकापेक्षा जास्त वेळा लागू केल्यास ते एकदा लागू केल्याप्रमाणेच परिणाम मिळायला हवे. - समान परिणाम मोजण्याचा एक वेगळा, सोपा मार्ग आहे का? आपण हे तपासू शकता की आपले जटिल, ऑप्टिमाइझ केलेले फंक्शन साध्या, स्पष्टपणे योग्य आवृत्तीप्रमाणेच आउटपुट तयार करते (उदा. Python च्या अंगभूत
sorted()
विरुद्ध आपल्या फॅन्सी क्रमवारीची चाचणी घेणे). - आउटपुटबद्दल नेहमी काय सत्य असले पाहिजे? (उदा. `find_prime_factors` फंक्शनच्या आउटपुटमध्ये फक्त मूळ संख्या असाव्यात आणि त्यांचे उत्पादन इनपुटच्या समान असले पाहिजे).
- राज्यात कसा बदल होतो? (स्टेटफुल चाचणीसाठी) कोणत्याही वैध ऑपरेशननंतर कोणते अपरिवर्तनीय राखले जाणे आवश्यक आहे? (उदा. शॉपिंग कार्टमधील वस्तूंची संख्या कधीही नकारात्मक असू शकत नाही).
निष्कर्ष: आत्मविश्वासाची एक नवीन पातळी
Hypothesis सह प्रॉपर्टी-आधारित चाचणी उदाहरण-आधारित चाचणीची जागा घेत नाही. गंभीर व्यवसाय तर्कशास्त्र आणि चांगल्या प्रकारे समजलेल्या आवश्यकतांसाठी आपल्याला अजूनही विशिष्ट, हाताने लिहिलेल्या चाचण्यांची आवश्यकता आहे (उदा. "देश X मधील वापरकर्त्याला किंमत Y दिसणे आवश्यक आहे").Hypothesis जे प्रदान करते ते आपल्या कोडचे वर्तन शोधण्याचा आणि अप्रत्याशित एज केसेसपासून बचाव करण्याचा एक शक्तिशाली, स्वयंचलित मार्ग आहे. हे एका अथक भागीदारासारखे कार्य करते, हजारो चाचण्या व्युत्पन्न करते ज्या कोणत्याही मानवापेक्षा अधिक वैविध्यपूर्ण आणि विक्षिप्त आहेत. आपल्या कोडचे मूलभूत गुणधर्म परिभाषित करून, आपण एक मजबूत तपशील तयार करता ज्याच्या विरूद्ध Hypothesis चाचणी घेऊ शकते, ज्यामुळे आपल्याला आपल्या सॉफ्टवेअरमध्ये आत्मविश्वासाची एक नवीन पातळी मिळते.
पुढच्या वेळी आपण फंक्शन लिहिता, तेव्हा उदाहरणांच्या पलीकडे विचार करण्यासाठी थोडा वेळ काढा. स्वतःला विचारा, "नियम काय आहेत? नेहमी काय सत्य असले पाहिजे?" मग, त्यांना खंडित करण्याचे कठोर परिश्रम Hypothesis ला करू द्या. त्यास काय सापडते याने आपल्याला आश्चर्य वाटेल आणि आपला कोड त्याहून चांगला होईल.